﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;


namespace SilverBox.Physics
{
    public class BodyControl : Control, IShapeProvider {

        public static readonly DependencyProperty InitialRotationalVelocityProperty = DependencyProperty.Register("InitialRotationalVelocity", typeof(double), typeof(BodyControl), new PropertyMetadata(0.0));
        public static readonly DependencyProperty DensityProperty = DependencyProperty.Register("Density", typeof(double), typeof(BodyControl), new PropertyMetadata(1.0));
        public static readonly DependencyProperty IsStaticProperty = DependencyProperty.Register("IsStatic", typeof(bool), typeof(BodyControl), new PropertyMetadata(null));
        public static readonly DependencyProperty RestitutionProperty = DependencyProperty.Register("Restitution", typeof(double), typeof(BodyControl), new PropertyMetadata(0.1));
        public static readonly DependencyProperty FrictionProperty = DependencyProperty.Register("Friction", typeof(double), typeof(BodyControl), new PropertyMetadata(0.3));
        public static readonly DependencyProperty InitialVelocityProperty = DependencyProperty.Register("InitialVelocity", typeof(Vector), typeof(BodyControl), new PropertyMetadata(new Vector(0, 0)));

        private Entity entity;
        private PhysicsContext context;

        public BodyControl() {
            this.DefaultStyleKey = typeof(BodyControl);
            this.entity = new Entity(this);

            this.Entity.Collide += delegate(object sender, CollideEventArgs e) {
                                                                                   this.OnCollide(e);
            };

            this.Entity.BoundaryViolation += delegate(object sender, EventArgs e) {
                                                                                      this.OnBoundaryViolation(e);
            };
        }

        public EventHandler<CollideEventArgs> Collide;

        public FrameworkElement BodyShape {
            get {
                return this.GetTemplateChild("BodyShape") as FrameworkElement;
            }
        }

        public double Density {
            get { return (double)this.GetValue(BodyControl.DensityProperty); }
            set { this.SetValue(BodyControl.DensityProperty, value); }
        }

        public bool IsStatic {
            get { return (bool)this.GetValue(BodyControl.IsStaticProperty); }
            set { this.SetValue(BodyControl.IsStaticProperty, value); }
        }

        public double Restitution {
            get { return (double)this.GetValue(BodyControl.RestitutionProperty); }
            set { this.SetValue(BodyControl.RestitutionProperty, value); }
        }

        public double Friction {
            get { return (double)this.GetValue(BodyControl.FrictionProperty); }
            set { this.SetValue(BodyControl.FrictionProperty, value); }
        }

        public Vector InitialVelocity {
            get { return (Vector)this.GetValue(BodyControl.InitialVelocityProperty); }
            set { this.SetValue(BodyControl.InitialVelocityProperty, value); }
        }

        public double InitialRotationalVelocity {
            get { return (double)this.GetValue(BodyControl.InitialRotationalVelocityProperty); }
            set { this.SetValue(BodyControl.InitialRotationalVelocityProperty, value); }
        }


        public Entity Entity {
            get { return this.entity; }
        }

        public void RemoveEntity() {
            this.Entity.Simulator.RemoveEntity(this.Entity);
        }

        public override void OnApplyTemplate() {
            base.OnApplyTemplate();

            BodyParameters bodyParams = new BodyParameters() {
                                                                 Density = this.Density,
                                                                 Friction = this.Friction,
                                                                 IsStatic = this.IsStatic,
                                                                 Restitution = this.Restitution,
                                                                 InitialVelocity = this.InitialVelocity,
                                                                 InitialRotationalVelocity = this.InitialRotationalVelocity,
                                                             };

            if (!DesignerProperties.GetIsInDesignMode(this)) {

                this.Dispatcher.BeginInvoke(delegate {

                                                         this.Simulator.AddEntity(this.Entity, bodyParams);
                });
            }
        }

        protected virtual void OnCollide(CollideEventArgs e) {
            if (this.Collide != null)
                this.Collide(this, e);
        }

        protected virtual void OnBoundaryViolation(EventArgs e) {
        }

        protected Simulator Simulator {
            get {
                if (this.context == null) {
                    this.TryGetContext();
                }
                return this.context.Simulator;
            }
        }

        private void TryGetContext() {
            PhysicsContext context = PhysicsContext.FindContext(this);
            if (context == null) {
                context = new PhysicsContext();
                PhysicsContext.SetPhysicsContext(Application.Current.RootVisual, context);
            }

            this.context = context;
        }


    }
}